from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
import json
import time
import re
import random

class MenuScraper:
    def __init__(self):
        chrome_options = Options()
        # 移除无头模式，使用真实浏览器
        # chrome_options.add_argument('--headless')

        # 添加更多的伪装参数
        chrome_options.add_argument('--disable-gpu')
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        chrome_options.add_argument('--disable-blink-features=AutomationControlled')
        chrome_options.add_argument('--window-size=1920,1080')
        chrome_options.add_argument('--start-maximized')
        chrome_options.add_argument('--ignore-certificate-errors')
        chrome_options.add_argument('--allow-running-insecure-content')

        # 添加更真实的 User-Agent
        chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')

        # 添加更多的实验性选项
        chrome_options.add_experimental_option('excludeSwitches', ['enable-automation', 'enable-logging'])
        chrome_options.add_experimental_option('useAutomationExtension', False)
        chrome_options.add_experimental_option('prefs', {
            'profile.default_content_setting_values': {
                'images': 1,  # 允许加载图片
                'javascript': 1,
                'cookies': 1,
                'plugins': 1,
                'popups': 1
            }
        })

        self.driver = webdriver.Chrome(options=chrome_options)
        self.driver.set_page_load_timeout(30)

        # 添加更多的 stealth 设置
        self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
            'source': '''
                Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
                Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
                Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
                window.chrome = { runtime: {} };
                Object.defineProperty(navigator, 'permissions', {get: () => {return {query: async () => ({state: 'granted'})}}});
            '''
        })

    def scrape_menu(self, url):
        try:
            self.driver.get(url)
            time.sleep(10)  # 等待页面加载

            # 打印页面源码和当前URL，用于调试
            print("Current URL:", self.driver.current_url)
            print("Page source:", self.driver.page_source)

            # 先尝试定位一些基本元素，看看页面结构
            elements = self.driver.find_elements(By.XPATH, "//*")
            print("Available elements:")
            for element in elements[:10]:  # 只打印前10个元素
                print(f"Tag: {element.tag_name}, Class: {element.get_attribute('class')}, ID: {element.get_attribute('id')}")

            # 获取数据
            try:
                rd_info = self._get_restaurant_info()
            except Exception as e:
                print(f"Error getting restaurant info: {e}")
                rd_info = self._get_default_restaurant_info()

            try:
                dishes_series = self._get_dishes_series()
            except Exception as e:
                print(f"Error getting dishes series: {e}")
                dishes_series = []

            try:
                dishes_list = self._get_dishes_list()
            except Exception as e:
                print(f"Error getting dishes list: {e}")
                dishes_list = []

            menu_data = {
                "rd_info": rd_info,
                "dishes_series": dishes_series,
                "dishes_list": dishes_list
            }

            return menu_data

        except Exception as e:
            print(f"Error scraping menu: {e}")
            raise
        finally:
            self.driver.quit()

    def _get_restaurant_info(self):
        """获取餐厅基本信息"""
        try:
            # 获取餐厅名称
            name = self.driver.find_element(By.CLASS_NAME, "pageHeader_storeName__-8K3m").text

            # 获取菜品分类
            categories = self.driver.find_elements(By.CLASS_NAME, "mainPage_menuPanelItem__2tsAj")

            rd_info = {
                "name": name,
                "telephone": "",  # 需要从其他地方获取
                "faxphone": "",
                "address_state": "",
                "address_county": "",
                "address_street": "",
                "zipcode": "",
                "monday": "",
                "tuesday": "",
                "wednesday": "",
                "thursday": "",
                "friday": "",
                "saturday": "",
                "sunday": "",
                "wait_time": "30",
                "detail": "",
                "image_url": "",
                "tax_rate": "0.08",
                "logo_url": "",
                "tip_ratio": '[{"ratio": "0.1500", "id": 1}, {"ratio": "0.18", "id": 2}, {"ratio": "0.20", "id": 3}, {"ratio": "0.25", "id": 4}]',
                "delivery": "1",
                "restaurant_pick_up_mode": {"distance": 5.00, "lowest_price": 10.00}
            }
            return rd_info
        except Exception as e:
            print(f"Error in _get_restaurant_info: {e}")
            return self._get_default_restaurant_info()

    def _get_dishes_series(self):
        """获取菜品分类"""
        dishes_series = []
        try:
            # 获取所有分类
            categories = self.driver.find_elements(By.CLASS_NAME, "mainPage_menuPanelItem__2tsAj")

            for index, category in enumerate(categories):
                category_data = {
                    "index": index,
                    "name": category.text,
                    "description": "",  # 需要从其他地方获取
                    "monday": "",
                    "tuesday": "",
                    "wednesday": "",
                    "thursday": "",
                    "friday": "",
                    "saturday": "",
                    "sunday": ""
                }
                dishes_series.append(category_data)

            return dishes_series
        except Exception as e:
            print(f"Error in _get_dishes_series: {e}")
            return []

    def _get_dishes_list(self):
        """获取所有菜品信息"""
        dishes_list = []
        try:
            dishes = self.driver.find_elements(By.CLASS_NAME, "mainPage_itemBody__2FZ6t")
            total_dishes = len(dishes)
            print(f"Found {total_dishes} dishes")

            for index, dish in enumerate(dishes):
                try:
                    print(f"Processing dish {index + 1}/{total_dishes}")

                    # 重新获取元素以避免stale element
                    try:
                        dishes = self.driver.find_elements(By.CLASS_NAME, "mainPage_itemBody__2FZ6t")
                        dish = dishes[index]
                    except:
                        continue

                    # 获取基本信息
                    try:
                        name_en = dish.find_element(By.CLASS_NAME, "mainPage_itemName__25d01").text
                        price = dish.find_element(By.CLASS_NAME, "mainPage_itemDisplayPrice__LLTWN").text.replace("$", "").strip()
                    except:
                        continue

                    # 获取描述和图片（非必需）
                    description = ""
                    image_url = ""
                    try:
                        description = dish.find_element(By.CLASS_NAME, "mainPage_itemDescription__3LZPD").text
                    except:
                        pass

                    try:
                        img_div = dish.find_element(By.CLASS_NAME, "mainPage_itemImg__1WY92")
                        style = img_div.get_attribute("style")
                        if "background-image" in style:
                            image_url = style.split('url(\'')[1].split('\')')[0]
                    except:
                        pass

                    # 尝试获取规格信息
                    specs = []
                    try:
                        # 确保元素可见和可点击
                        self.driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", dish)
                        time.sleep(0.5)

                        # 点击菜品打开弹窗
                        self.driver.execute_script("arguments[0].click();", dish)

                        # 获取规格信息
                        specs = self._get_dish_specs()

                    except Exception as e:
                        print(f"Error getting specs for dish {index + 1}: {e}")
                    finally:
                        # 关闭弹窗
                        try:
                            self._close_modal()
                            time.sleep(0.5)  # 等待弹窗完全关闭
                        except:
                            pass

                    dish_data = {
                        "name_en": name_en,
                        "name_ch": self._get_chinese_name(name_en),
                        "price": price,
                        "dishes_serice": "",
                        "signature_dishes": 0,
                        "image_url": image_url,
                        "detail": description,
                        "diches_specs": specs
                    }
                    dishes_list.append(dish_data)

                except Exception as e:
                    print(f"Error processing dish {index + 1}: {e}")
                    continue

            return dishes_list
        except Exception as e:
            print(f"Error in _get_dishes_list: {e}")
            return []

    def _get_dish_specs(self):
        """获取菜品规格信息"""
        specs = []
        try:
            # 等待弹窗和规格面板加载
            try:
                WebDriverWait(self.driver, 5).until(
                    EC.presence_of_element_located((By.CLASS_NAME, "mainPage_itemModal__1Aq-K"))
                )
                WebDriverWait(self.driver, 5).until(
                    EC.presence_of_element_located((By.CLASS_NAME, "mainPage_specificationPanel__1WVIM"))
                )
            except Exception as e:
                print(f"Error waiting for modal or spec panel: {e}")
                return []

            # 获取所有规格组
            specs_groups = self.driver.find_elements(By.CLASS_NAME, "mainPage_specificationGroup__1Wen7")
            if not specs_groups:
                print("No specification groups found.")
                return []

            for group in specs_groups:
                try:
                    # 获取规格组名称
                    group_name = group.find_element(By.CLASS_NAME, "mainPage_groupTitle__2P8ym").text

                    # 获取规格组描述，用于判断是否多选
                    group_desc = ""
                    try:
                        group_desc = group.find_element(By.CLASS_NAME, "mainPage_groupDesc__1BNTZ").text.lower()
                    except:
                        pass

                    # 获取选项列表
                    options = group.find_elements(By.CLASS_NAME, "mainPage_specificationItem__1VIMG")
                    if not options:
                        continue

                    specs_list = []
                    for i, option in enumerate(options, 1):
                        try:
                            # 获取选项名称
                            option_name = option.find_element(By.CLASS_NAME, "mainPage_itemName__25d01").text

                            # 获取选项价格
                            price = "0.0"
                            try:
                                price_element = option.find_element(By.CLASS_NAME, "mainPage_itemPrice__2Lp0V")
                                price_text = price_element.text.strip()
                                if price_text:
                                    price = price_text.replace("+$", "").replace("$", "").strip()
                            except:
                                pass

                            option_data = {
                                "name": option_name,
                                "money": price,
                                "id": i,
                                "count": 1
                            }
                            specs_list.append(option_data)
                        except Exception as e:
                            print(f"Error processing option: {e}")
                            continue

                    if not specs_list:
                        continue

                    # 判断是否多选和最大选择限制
                    is_multiple = False
                    max_options = len(specs_list)

                    # 从描述文本判断是否多选
                    if group_desc:
                        is_multiple = any(x in group_desc for x in [
                            "select up to",
                            "choose up to",
                            "maximum",
                            "select multiple",
                            "choose multiple"
                        ])

                        # 尝试获取最大选择数量
                        if is_multiple:
                            max_match = re.search(r'(\d+)', group_desc)
                            if max_match:
                                max_options = int(max_match.group(1))

                    # 从选项类型判断是否多选
                    if not is_multiple:
                        try:
                            checkboxes = group.find_elements(By.CLASS_NAME, "mainPage_checkbox__1WQFV")
                            is_multiple = len(checkboxes) > 0
                        except:
                            pass

                    spec_data = {
                        "specs_name": group_name,
                        "multi_select": 1 if is_multiple else 0,
                        "is_max_optins": 1 if is_multiple else 0,
                        "max_options": max_options,
                        "min_options": 0,
                        "specs_addition": 0,
                        "default_id": 1,
                        "detail": "",
                        "specs_list": specs_list
                    }
                    specs.append(spec_data)

                except Exception as e:
                    print(f"Error processing spec group: {e}")
                    continue

        except Exception as e:
            print(f"Error getting specs: {e}")

        return specs

    # 辅助方法
    def _get_business_hours(self, day):
        try:
            hours = self.driver.find_element(By.CLASS_NAME, f"hours-{day}").text
            if hours:
                match = re.search(r'(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})', hours)
                if match:
                    return f'[{{"end": "{match.group(2)}", "start": "{match.group(1)}"}}]'
            return ""
        except:
            return ""

    def _get_chinese_name(self, text):
        """从文本中提取中文名称"""
        match = re.search(r'[\u4e00-\u9fff]+', text)
        return match.group() if match else ""

    def _get_specs_options(self, group):
        """获取规格选项列表"""
        options = []
        try:
            for i, option in enumerate(group.find_elements(By.CLASS_NAME, "option"), 1):
                price_text = option.find_element(By.CLASS_NAME, "option-price").text
                price = re.sub(r'[^\d.]', '', price_text) if price_text else "0.0"

                option_data = {
                    "name": option.find_element(By.CLASS_NAME, "option-name").text,
                    "money": price,
                    "id": i,
                    "count": 1
                }
                options.append(option_data)
        except:
            pass
        return options

    def _has_max_options(self, group):
        try:
            return "max-options" in group.get_attribute("class")
        except:
            return False

    def _get_max_options(self, group):
        try:
            return int(group.get_attribute("data-max-options"))
        except:
            return len(group.find_elements(By.CLASS_NAME, "option"))

    def _get_min_options(self, group):
        try:
            return int(group.get_attribute("data-min-options"))
        except:
            return 0

    def _get_default_option_id(self, group):
        try:
            default = group.find_element(By.CLASS_NAME, "option.default")
            options = group.find_elements(By.CLASS_NAME, "option")
            return list(options).index(default) + 1
        except:
            return 0

    def _get_default_restaurant_info(self):
        """返回默认的餐厅信息"""
        return {
            "name": "",
            "telephone": "",
            "faxphone": "",
            "address_state": "",
            "address_county": "",
            "address_street": "",
            "zipcode": "",
            "monday": "",
            "tuesday": "",
            "wednesday": "",
            "thursday": "",
            "friday": "",
            "saturday": "",
            "sunday": "",
            "wait_time": "30",
            "detail": "",
            "image_url": "",
            "tax_rate": "0.08",
            "logo_url": "",
            "tip_ratio": '[{"ratio": "0.1500", "id": 1}, {"ratio": "0.18", "id": 2}, {"ratio": "0.20", "id": 3}, {"ratio": "0.25", "id": 4}]',
            "delivery": "1",
            "restaurant_pick_up_mode": {"distance": 5.00, "lowest_price": 10.00}
        }

    def _close_modal(self):
        """关闭弹窗的方法"""
        max_retries = 3
        for retry in range(max_retries):
            try:
                # 尝试点击关闭按钮
                close_buttons = self.driver.find_elements(By.CLASS_NAME, "mainPage_closeBtn__1Yp4f")
                if close_buttons:
                    self.driver.execute_script("arguments[0].click();", close_buttons[0])
                    time.sleep(0.5)
                    return True
            except:
                pass

            try:
                # 尝试按ESC键
                webdriver.ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
                time.sleep(0.5)
                return True
            except:
                pass

            try:
                # 尝试点击页面空白处
                body = self.driver.find_element(By.TAG_NAME, "body")
                self.driver.execute_script("arguments[0].click();", body)
                time.sleep(0.5)
                return True
            except:
                pass

            if retry < max_retries - 1:
                time.sleep(1)

        return False

if __name__ == "__main__":
    url = "https://order.mealkeyway.com/customer/release/index?mid=306f4e69557367556d4a2f334a53456663354b6147773d3d#/main"
    scraper = MenuScraper()
    menu_data = scraper.scrape_menu(url)

    # 保存为JSON文件
    with open("menu_data.json", "w", encoding="utf-8") as f:
        json.dump(menu_data, f, ensure_ascii=False, indent=4)